package com.atguigu.gmall.product.service.impl;

import com.atguigu.gmall.product.service.TestLockService;
import io.swagger.models.auth.In;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Author: 孙润达
 * @Date: 2022/1/10 19:00
 */
@Service
public class TestLockServiceImpl implements TestLockService {
    private final Lock lock =new ReentrantLock();
    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public void testLock() {
      /*  lock.lock();
        //上锁
        try {
            Integer num=(Integer)redisTemplate.opsForValue().get("num");
            num++;
            System.out.println("num = " + num);
        } finally {
            lock.unlock();
        }*/
        //分布式锁 redis setnx --->java:setIfAbsent
        //设置锁过期时间10秒
        UUID uuid = UUID.randomUUID();
        Boolean isLock = redisTemplate.opsForValue().setIfAbsent("state", uuid,10, TimeUnit.SECONDS);
        if (isLock) {
            try {
                Integer num = (Integer) redisTemplate.opsForValue().get("num");
                num++;
                System.out.println("num = " + num);
            } finally {
           /*     解锁的时候根据key值获取value值然后进行对比，
                 这样可以防止程序运行时间超过锁失效的时间，从而导致之前的线程再解锁的时候解的是别人的锁
                 UUID:防误删
                 LUA：脚本保证原子性,call调取命令
                 "if redis.call('get', KEYS[1]) == ARGV[1] then return tostring(redis.call('del',KEYS[1])) else return 0 end"
           * */
                  /*if(uuid.equals(redisTemplate.opsForValue().get("state"))){
                      redisTemplate.delete("state");
                  }*/
                String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
               //执行上面的LUA脚本会在redis上执行（redis是单线程（从而保证原子性））
                redisTemplate.execute(new DefaultRedisScript<>(script), Collections.singletonList("state"), uuid);

            }
        }else {
            //未拿到锁,再次拿锁
            testLock();
        }
    }
}
